package rs.thread.processor;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import rs.domain.Track;
import rs.parser.TagsParser;
import rs.thread.OnerousProcessor;
import rs.util.Requester;
import rs.util.TagRepository;

/* Classe che, dato un brano, ne recupera i tag. 
 * Il task in questione prevede l'uso di un repository che fa riferimento ad un database.
 * Essendo i tag di un brano una caratteristica abbastanza stabile nel tempo, li si inserisce in un db.
 * Il tal modo, se si conoscono già i tag per un certo brano, si risparmia una richiesta e il relativo parsing. */
public class TagProcessor implements OnerousProcessor<Track> {

	private Map<Track, List<String>> track2tags;
	private TagRepository repository;
	private TagsParser parser;
	private Lock lock;

	public TagProcessor() {
		this.track2tags = new HashMap<Track, List<String>>();
		this.repository = new TagRepository();
		this.parser = new TagsParser();
		this.lock = new ReentrantLock();
	}

	@Override
	public void onerousFunction(Requester app, Track t) {
		List<String> tags = this.track2tags.get(t);		
		
		if (tags == null) {
			/* Il lock è necessario, altrimenti:
			 * 		- due thread t1 e t2 sono in cerca dei tag di uno stesso brano
			 * 		- nessuno dei due li trova nel database e ognuno invia una richiesta a Last.fm (inefficiente)
			 * 		- t1 riceve la risposta per primo e inserisce i tag nel database
			 * 		- t2 cerca di inserire i tag in un secondo momento, ma non può per il vincolo di chiave (esiste già una tupla per quel brano) */
			this.lock.lock();
			tags = this.repository.getTags(t);
			
			if (tags == null) {
				String url = 
						"http://ws.audioscrobbler.com/2.0/?" +
						"method=track.gettoptags&" +
						"artist=" + t.getArtist() + "&" +
						"track=" + t.getName() + "&" +
						"api_key=8cfeab56c4060f4c3b983902e3903a80&" +
						"format=json";
				tags = this.parser.parse(app.send(url));
				this.track2tags.put(t, tags);
				this.repository.addTags(t, tags);
			}
			else {
				this.track2tags.put(t, tags);
			}
			this.lock.unlock();
		}
	}

	public Map<Track, List<String>> getResult() {
		return this.track2tags;
	}

}